# -*- coding: utf-8 -*-
from Axon.Component import component
from Axon.Component import newComponent
from Axon.Box import makeOutbox
from Kamaelia.Util.Backplane import PublishTo
from Kamaelia.UI.Pygame.Display import PygameDisplay
from Kamaelia.UI.Pygame.Ticker import Ticker
from pygame import surface
from pygame import sprite
from pygame import image
from pygame import time
from pygame import font

BOARD_GRID_SIZE = 32
BOARD_SIZE_X = 10
BOARD_SIZE_Y = 10

RENDER_CREATE_SPHERE = "createSphere" #,type,x,y
RENDER_REMOVE_SPHERE = "removeSphere" #,x,y
RENDER_MOVE_SPHERE = "moveSphere" #,x,y,newX,newY
RENDER_SWAP_SPHERE = "swapSphere" #,x1,y1,x2,y2

RENDER_CREATE_CURSOR= "createCursor" #playerId,x,y,
RENDER_REMOVE_CURSOR = "removeCursor" #,playerId
RENDER_MOVE_CURSOR = "moveCursor" #,playerId,newX,newY
RENDER_SET_SCORE= "setScore" #playerId,score


class UpdateGenerator(component):
    " Ticks every n milisecond with update message "
    Inboxes = {"control" : ""}
    Outboxes = {"outbox" : ""}
    
    def __init__(self,delay):
        self.__super.__init__() # Must always happen!
        self.delay = delay
        self.lastTick = time.get_ticks()
        
    def main(self):
        while True:
            yield 1
            actTime = time.get_ticks()
            if(actTime - self.lastTick >= self.delay):
                self.send(actTime - self.lastTick,"outbox")
                self.lastTick = actTime 

class SphereSprite(sprite.Sprite):
    """ Sprite representing sphere """
    sphereImages = { 0 : "data/sphere/0.png",
                   1 : "data/sphere/1.png",
                   2 : "data/sphere/2.png",
                   3 : "data/sphere/3.png",
                   4 : "data/sphere/4.png",
                   5 : "data/sphere/5.png",}
    moveSpeed = 100
        
    def __init__(self,type,boardX,boardY):
        sprite.Sprite.__init__(self)
        self.type = type
        self.boardX = boardX
        self.boardY = boardY
        self.posX = BOARD_GRID_SIZE * boardX
        self.posY = BOARD_GRID_SIZE * boardY
        self.image = image.load(self.sphereImages[type])
        self.rect = self.image.get_rect()
        self.rect.topleft= (self.posX,self.posY)
        
        
    def process(self,timeStep):
        change = False
        bPosX = (BOARD_GRID_SIZE * self.boardX)
        bPosY = (BOARD_GRID_SIZE * self.boardY)
        step = ((timeStep / 1000.0) * self.moveSpeed)
                        
        if (bPosX != self.posX):
            change = True
            diffX = abs(bPosX - self.posX)
            if(diffX <= step):
                self.posX = bPosX
            else:                
                if bPosX < self.posX:
                    self.posX -= step
                else:
                    self.posX += step 
        if (bPosY != self.posY):
            change = True
            diffY = abs(bPosY - self.posY)
            if(diffY <= step):
                self.posY = bPosY
            else:                
                if bPosY < self.posY:
                    self.posY -= step
                else:
                    self.posY += step
                    
        if(change):
            self.rect.topleft= (self.posX,self.posY)               
        return change

class CursorSprite(sprite.Sprite):
    """ Sprite representing player cursor """
    cursorImage = "data/cursor.png"
        
    def __init__(self,playerId,boardX,boardY):
        sprite.Sprite.__init__(self)
        self.playerId = playerId
        self.boardX = boardX
        self.boardY = boardY
        self.image = image.load(self.cursorImage)
        self.rect = self.image.get_rect()
        self.rect.topleft= (BOARD_GRID_SIZE * self.boardX,BOARD_GRID_SIZE * self.boardY)
        
    def process(self,timeStep):
        bPosX = (BOARD_GRID_SIZE * self.boardX)
        bPosY = (BOARD_GRID_SIZE * self.boardY)
                                
        if (bPosX != self.rect.topleft[0] or 
            bPosY != self.rect.topleft[1]):
            self.rect.topleft= (BOARD_GRID_SIZE * self.boardX,BOARD_GRID_SIZE * self.boardY)
            return True
        return False
    
    
class LeftPanelRenderer(component):
    Inboxes = {"control" : "",
               "displayInbox" : "", 
               "displayCallback" : "",
               "scoreInbox" : "",}
    Outboxes = {"displaySignal" : ""}
    
    def __init__(self):
        self.__super.__init__() # Must always happen!
        self.font = font.Font(font.get_default_font(),15)
        self.scores = {}
         
    def main(self):
        """ Main loop """        
        displayservice = PygameDisplay.getDisplayService()
        self.link((self,"displaySignal"), displayservice)
        self.send( { "DISPLAYREQUEST" : True,
                    "callback" : (self,"displayCallback"),
                    "events" : (self, "displayInbox"),
                    "size": (140,480),
                    "position" : (0,0)},
                    "displaySignal")
        while not self.dataReady("displayCallback"):
            yield 1
        self.display = self.recv("displayCallback")
                                             
        # Main loop
        done = False

        while not done:          
            # We are ready to update?
            if self.dataReady("scoreInbox"):
                playerId,score = self.recv("scoreInbox")
                self.scores[playerId] = score
                
                self.display.fill((0,0,0))
                y = 0
                for playerId in self.scores:
                    textSurf = self.font.render("Player " + str(playerId) + ": " + str(self.scores[playerId]),
                                     True,(255,255,255))
                    self.display.blit(textSurf,(5,y))
                    y+=20
                    
                self.send({"REDRAW":True, "surface":self.display}, "displaySignal")
                   
            yield 1
     
     
class  BoardRenderer(component):
    Inboxes = {"control" : "", 
               "displayCallback" : "",
               "updateInbox" : "",
               "renderInbox" : "",
               "displayInbox" : ""}
    Outboxes = {"displaySignal" : "",
                "scoreOutbox" : "",}
    
    backgroundCellFile = "data/boardCell.png"
    
    def __init__(self):
        self.__super.__init__() # Must always happen!
        self.ballSpritesGroup = sprite.RenderPlain()
        self.cursorSpritesGroup = sprite.RenderPlain()
        self.leftPanel = LeftPanelRenderer()
                                        
    def generateBackground(self):
        """ Generate board background """
        backgroundCell = image.load(self.backgroundCellFile)
        self.backgroundSurface = self.display.convert()
        for x in xrange(0,BOARD_SIZE_X):
            for y in xrange(0,BOARD_SIZE_Y):
                self.backgroundSurface.blit(backgroundCell,
                                (BOARD_GRID_SIZE*x,BOARD_GRID_SIZE*y))
        
    def main(self):
        """ Main loop """
        # Get drawing surface
        displayservice = PygameDisplay.getDisplayService()
        self.link((self,"displaySignal"), displayservice)
        self.send( { "DISPLAYREQUEST" : True,
                    "callback" : (self,"displayCallback"),
                    "events" : (self, "displayInbox"),
                    "size": (BOARD_GRID_SIZE * BOARD_SIZE_X,BOARD_GRID_SIZE * BOARD_SIZE_Y,),
                    "position" : (140,80)},
                    "displaySignal")
        while not self.dataReady("displayCallback"):
            yield 1
        self.display = self.recv("displayCallback")
        
        # Create left panel
        self.leftPanel.activate()
        self.addChildren(self.leftPanel)
        self.link((self,"scoreOutbox"),(self.leftPanel,"scoreInbox"))
        # yield newComponent(self.leftPanel)
        
        # Create background
        self.generateBackground()
                                           
        # Main loop
        done = False
        change = True

        while not done:          
            # We are ready to update?
            if self.dataReady("updateInbox"):
                change = change or self.process(self.recv("updateInbox"))
                
            # New command here
            while self.dataReady("renderInbox"):
                change = True
                self.renderCmd(self.recv("renderInbox"))
                                    
            # Draw??
            if change:                
                self.display.blit(self.backgroundSurface,(0,0))
                self.ballSpritesGroup.draw(self.display)
                self.cursorSpritesGroup.draw(self.display)
                self.send({"REDRAW":True, "surface":self.display}, "displaySignal")
                change = False
                   
            yield 1
            
    def process(self,timeStep):
        """ Move sprites progress """
        change = False
        for ballSprite in self.ballSpritesGroup.sprites():
            if(ballSprite.process(timeStep)):
                change = True
        for cursorSprite in self.cursorSpritesGroup.sprites():
            if(cursorSprite.process(timeStep)):
                change = True                
        return change
            
    def getSphere(self,x,y):
        """ Get sphere from given position or null """
        for sphereSprite in self.ballSpritesGroup.sprites():
            if(sphereSprite.boardX == x and sphereSprite.boardY == y):
                return sphereSprite
        return None

    def getCursor(self,playerId):
        """ Get cursor of given player or null """
        for cursorSprite in self.cursorSpritesGroup.sprites():
            if(cursorSprite.playerId == playerId):
                return cursorSprite
        return None
    
            
    def renderCmd(self,cmd):
        print "Render ",cmd
        """ Interpret a command """
        if cmd[0] == RENDER_CREATE_SPHERE:
            type = cmd[1]
            x = cmd[2]
            y = cmd[3]            
            self.ballSpritesGroup.add(SphereSprite(type,x,y))                    
        elif cmd[0] == RENDER_REMOVE_SPHERE:
            x = cmd[1]
            y = cmd[2]
            self.ballSpritesGroup.remove(self.getSphere(x,y))
        elif cmd[0] == RENDER_MOVE_SPHERE:
            x = cmd[1]
            y = cmd[2]
            nx = cmd[3]
            ny = cmd[4]
            sph = self.getSphere(x,y)
            sph.boardX = nx
            sph.boardY = ny
        elif cmd[0] == RENDER_SWAP_SPHERE:
            x1 = cmd[1]
            y1 = cmd[2]
            x2 = cmd[3]
            y2 = cmd[4]
            sph1 = self.getSphere(x1,y1)
            sph2 = self.getSphere(x2,y2)
            sph1.boardX = x2
            sph1.boardY = y2
            sph2.boardX = x1
            sph2.boardY = y1
        elif cmd[0] == RENDER_CREATE_CURSOR:
            playerId = cmd[1]
            x = cmd[2]
            y = cmd[3]            
            self.cursorSpritesGroup.add(CursorSprite(playerId,x,y))
            self.send((playerId,0), "scoreOutbox")            
        elif cmd[0] == RENDER_REMOVE_CURSOR:            
            playerId = cmd[1]
            self.cursorSpritesGroup.remove(self.getCursor(playerId))
        elif cmd[0] == RENDER_MOVE_CURSOR:
            playerId = cmd[1]
            nx = cmd[2]
            ny = cmd[3]
            cursor = self.getCursor(playerId)
            cursor.boardX = nx
            cursor.boardY = ny
        elif cmd[0] == RENDER_SET_SCORE:
            playerId = cmd[1]
            score = cmd[2]
            self.send((playerId,score), "scoreOutbox")                                    